#! /usr/bin/python
	
#* ========================================================================= *#
#* ------------------------------------------------------------------------- *#
##
##  \file		vs2010.py
##  \date		Jully 2011
##  \author		TNick
##
##  \brief		Script for creating a Visual Studio 2010 solution from a .pro
##
##
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @n
## Please read ReadMe.txt and Licence.txt in root folder @n
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @n
##
#* ------------------------------------------------------------------------- *#
#* ========================================================================= *#
##
##
##
##
#*  INCLUDES    ------------------------------------------------------------ *#
import	fileinput, string, sys
import	os
from	time import gmtime, strftime
import	uuid
import	string
import	shutil




#*  INCLUDES    ============================================================ *#
##
##
##
##
#*  DATA    ---------------------------------------------------------------- *#

## used with templates to replace the ##AUTH## tag
g_s_auth = "TNick"

#*  DATA    ================================================================ *#
##
##
##
##
#*  FUNCTIONS    ----------------------------------------------------------- *#

#* ------------------------------------------------------------------------- *#
def	showLogo():
	""" Simply print an informative logo at the start of the program """

	print("===================================================")
	print("Utility to generate Visual Studio solution from a .pro")
	print("===================================================")
	print("")

#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def	getQtPrjFile( line, lst_out ):
	""" gets the stripped input from text; if ends with continuation lines sets b_cont
	"""
	b_cont = False
	if ( line.startswith( "#" ) ):
		return True
	
	if ( line.endswith( "\\" ) ):
		b_cont = True
		line = line[:-1].strip()
	
	if ( len( line ) > 0 ):
		lst_out.append( line )
		
	return b_cont
	
#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def	getQtPrjInfo( \
		prj, hd_lst, src_lst, res_lst, \
		form_lst, other_lst, inc_path, \
		config_type ):
	""" the function gets the content of a .pro file and extracts informations
	"""
	
	prj_lines = prj.split( "\n", 10000 )
	status = 0
	b_cont = False
	hd_mark = "HEADERS +="
	src_mark = "SOURCES +="	
	res_mark = "RESOURCES +="
	other_mark = "OTHER_FILES +="	
	form_mark = "FORMS +="	
	incp_mark = "INCLUDEPATH +="	
	template_mark = "TEMPLATE ="	
	config_mark = "CONFIG += "	
	cfg_data = []

	for prj_l in prj_lines:
		prj_l = prj_l.strip()
		if ( status == 0 ):
			if ( prj_l.startswith( hd_mark ) ):
				if ( getQtPrjFile( prj_l[ len( hd_mark ):].strip(), hd_lst ) ):
					status = 1
			elif ( prj_l.startswith( src_mark ) ):
				if ( getQtPrjFile( prj_l[len( src_mark ):].strip(), src_lst ) ):
					status = 2
			elif ( prj_l.startswith( res_mark ) ):
				if ( getQtPrjFile( prj_l[ len( res_mark ):].strip(), res_lst ) ):
					status = 3
			elif ( prj_l.startswith( other_mark ) ):
				if ( getQtPrjFile( prj_l[ len( other_mark ):].strip(), other_lst ) ):
					status = 4
			elif ( prj_l.startswith( form_mark ) ):
				if ( getQtPrjFile( prj_l[ len( form_mark ):].strip(), form_lst ) ):
					status = 5					
			elif ( prj_l.startswith( incp_mark ) ):
				if ( getQtPrjFile( prj_l[ len( incp_mark ):].strip(), inc_path ) ):
					status = 6					
			if ( prj_l.startswith( config_mark ) ):
				if ( getQtPrjFile( prj_l[ len( config_mark ):].strip(), cfg_data ) ):
					status = 7
			elif ( prj_l.startswith( template_mark ) ):
				tmpl = prj_l[ len( template_mark ):].strip().upper()
				if ( tmpl == "APP" ):
					config_type[0] = "Application"
				elif ( tmpl == "LIB" ):
					config_type[0] = "DynamicLibrary"
				else:
					config_type[0] = "Application"
					print("Warning! Unknown output type: " + tmpl)
		elif ( status == 1 ):
			if ( getQtPrjFile( prj_l, hd_lst ) == False ):
				status = 0
		elif ( status == 2 ):
			if ( getQtPrjFile( prj_l, src_lst ) == False ):
				status = 0
		elif ( status == 3 ):
			if ( getQtPrjFile( prj_l, res_lst ) == False ):
				status = 0
		elif ( status == 4 ):
			if ( getQtPrjFile( prj_l, other_lst ) == False ):
				status = 0
		elif ( status == 5 ):
			if ( getQtPrjFile( prj_l, form_lst ) == False ):
				status = 0
		elif ( status == 6 ):
			if ( getQtPrjFile( prj_l, inc_path ) == False ):
				status = 0
		elif ( status == 7 ):
			if ( getQtPrjFile( prj_l, cfg_data ) == False ):
				status = 0
			
			
	cfg_txt = " ".join( cfg_data ).upper().strip()
	cfg_data = cfg_txt.split()
	if ( "STATIC" in cfg_data ):
		if ( config_type[0] == "Application" ):
			print("Error in Qt project! Config is static library but template is application" )
		config_type[0] = "StaticLibrary"
	elif ( "SHARED" in cfg_data ):
		if ( config_type[0] == "Application" ):
			print("Error in Qt project! Config is shared library but template is application" )
		config_type[0] = "DynamicLibrary"
		
				
#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def	adjustFilePaths( \
prj_file, hd_lst, src_lst, res_lst, \
form_lst, other_lst, inc_path ):
	""" 
	"""
	base_p = os.path.dirname( prj_file ) + "/"

	i = 0
	for prj_l in hd_lst:
		hd_lst[i] =  os.path.abspath( base_p + prj_l )
		i=i+1
		
	i = 0
	for prj_l in src_lst:
		src_lst[i] =  os.path.abspath( base_p + prj_l )
		i=i+1
		
	i = 0
	for prj_l in res_lst:
		res_lst[i] =  os.path.abspath( base_p + prj_l )
		i=i+1
	
	i = 0
	for prj_l in form_lst:
		form_lst[i] =  os.path.abspath( base_p + prj_l )
		i=i+1
		
	i = 0
	for prj_l in other_lst:
		other_lst[i] =  os.path.abspath( base_p + prj_l )
		i=i+1
		
	i = 0
	for prj_l in inc_path:
		inc_path[i] =  os.path.abspath( base_p + prj_l )
		i=i+1

		
#* ========================================================================= *#
	
#* ------------------------------------------------------------------------- *#
def	filtFile( prj_path, hd_lst, src_lst, res_lst, form_lst, other_lst ):
	""" 
	"""
	

	outp = ""
	
	base_pth = os.path.abspath( "../../" )
	filt_list = []
	
	filt_pat = '''    <Filter Include="##FILTER##">
      <UniqueIdentifier>{##FGUID##}</UniqueIdentifier>
    </Filter>
'''
	
	incl_pat = '''    <ClInclude Include="##FILE_PATH##">
      <Filter>##FILTER##</Filter>
    </ClInclude>
'''
	src_pat = '''    <ClCompile Include="##FILE_PATH##">
      <Filter>##FILTER##</Filter>
    </ClCompile>
'''
	other_pat = '''    <None Include="##FILE_PATH##">
      <Filter>##FILTER##</Filter>
    </None>
'''
	
	filt_list.append( "Header Files" )
	filt_list.append( "Source Files" )	
	filt_list.append( "Other Files" )	
	incl_txt=""
	for prj_l in hd_lst:
		f_pth = prj_l.replace( base_pth, "" )
		if ( f_pth.startswith("\\") ):  f_pth = f_pth[1:]
		f_pth = "Header Files\\" + os.path.dirname( f_pth )
		filt_list.append( f_pth )
		flt_e_txt = incl_pat
		flt_e_txt = flt_e_txt.replace( "##FILE_PATH##", \
				os.path.relpath( prj_l, prj_path ) )
		flt_e_txt = flt_e_txt.replace( "##FILTER##", f_pth )
		incl_txt += flt_e_txt
		
	src_txt=""
	for prj_l in src_lst:
		f_pth = prj_l.replace( base_pth, "" )
		if ( f_pth.startswith("\\") ):  f_pth = f_pth[1:]
		f_pth = "Source Files\\" + os.path.dirname( f_pth )
		filt_list.append( f_pth )
		flt_e_txt = src_pat
		flt_e_txt = flt_e_txt.replace( "##FILE_PATH##", \
				os.path.relpath( prj_l, prj_path ) )
		flt_e_txt = flt_e_txt.replace( "##FILTER##", f_pth )
		src_txt += flt_e_txt
	
	other_txt=""
	for prj_l in res_lst:
		f_pth = prj_l.replace( base_pth, "" )
		if ( f_pth.startswith("\\") ):  f_pth = f_pth[1:]
		f_pth = "Other Files\\" + os.path.dirname( f_pth )
		filt_list.append( f_pth )
		flt_e_txt = other_pat
		flt_e_txt = flt_e_txt.replace( "##FILE_PATH##", \
				os.path.relpath( prj_l, prj_path ) )
		flt_e_txt = flt_e_txt.replace( "##FILTER##", f_pth )
		other_txt += flt_e_txt
	for prj_l in form_lst:
		f_pth = prj_l.replace( base_pth, "" )
		if ( f_pth.startswith("\\") ):  f_pth = f_pth[1:]
		f_pth = "Other Files\\" + os.path.dirname( f_pth )
		filt_list.append( f_pth )
		flt_e_txt = other_pat
		flt_e_txt = flt_e_txt.replace( "##FILE_PATH##", \
				os.path.relpath( prj_l, prj_path ) )
		flt_e_txt = flt_e_txt.replace( "##FILTER##", f_pth )
		other_txt += flt_e_txt
	for prj_l in other_lst:
		f_pth = prj_l.replace( base_pth, "" )
		if ( f_pth.startswith("\\") ):  f_pth = f_pth[1:]
		f_pth = "Other Files\\" + os.path.dirname( f_pth )
		filt_list.append( f_pth )
		flt_e_txt = other_pat
		flt_e_txt = flt_e_txt.replace( "##FILE_PATH##", \
				os.path.relpath( prj_l, prj_path ) )
		flt_e_txt = flt_e_txt.replace( "##FILTER##", f_pth )
		other_txt += flt_e_txt
	
	
	filt_list_txt = ""
	filt_list = list( set( filt_list ) )
	filt_list.sort()
	filt_list_gen = []
	filt_list_level = []
	for flt_e in filt_list:
		components = flt_e.split( '\\' )
		crt_comp = ""
		level = 1
		for cmp_iter in components:
			if ( level != 1 ):
				crt_comp += "\\"
			crt_comp += cmp_iter
			if ( crt_comp in filt_list_gen ):
				pass
			else:
				filt_list_gen.append( crt_comp )
				filt_list_level.append( level )
			level += 1
	
	i = 0
	while ( i < len( filt_list_gen ) ):
		# get the filter with the lowest level
		j = 1
		idx_now = 0
		min_val = filt_list_level[0]
		while ( j < len( filt_list_gen ) ):
			if ( filt_list_level[j] < min_val ):
				min_val = filt_list_level[j]
				idx_now = j
			j += 1
		filt_list_level[idx_now] = 1000000
		flt_e_txt = filt_pat
		flt_e_txt = flt_e_txt.replace( "##FILTER##", filt_list_gen[idx_now] )
		flt_e_txt = flt_e_txt.replace( "##FGUID##", str( uuid.uuid1() ) )
		filt_list_txt += flt_e_txt
		
		i += 1

	
	outp = \
	"  <ItemGroup>\n" + filt_list_txt + "  </ItemGroup>\n" + \
	"  <ItemGroup>\n" + incl_txt + "  </ItemGroup>\n" + \
	"  <ItemGroup>\n" + src_txt + "  </ItemGroup>\n" + \
	"  <ItemGroup>\n" + other_txt + "  </ItemGroup>"

	return outp

#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def	generateSol( sol_name, out_path, prj_files ):
	""" the function generates a solution based on input
	"""
	
	out_path = os.path.abspath( out_path )
	tools_path = os.path.dirname( os.path.abspath( os.path.dirname( __file__ ) )  )
	# templ_path = os.path.abspath( tools_path )
	templ_path = os.path.abspath( tools_path + os.sep + "templ" )
	print( "Output path is: " + out_path )
	print( "Solution name is: " + sol_name ) 
	print( "Tools path is: " + tools_path )
	print( "Template path is: " + templ_path )
	print( "\n" )
	
	# input the solution from file
	source_file = templ_path + os.sep + "XXSolXX.sln"
	file = None
	try:
		file = open(source_file, "r")
		sol_text = file.read()
		file.close()
		file = None
	except:
		print("Error!!! File " + source_file + " could not be readed!")
		if ( file != None ):
			file.close()
		return 1
		
	# input project template
	source_file = templ_path + os.sep + "XXPrjXX.vcxproj"
	file = None
	try:
		file = open(source_file, "r")
		prj_text_pat = file.read()
		file.close()
		file = None
	except:
		print("Error!!! File " + source_file + " could not be readed!")
		if (file != None):
			file.close()
		return 1
		
	# input project template
	source_file = templ_path + os.sep + "XXPrjXX.vcxproj.filters"
	file = None
	try:
		file = open(source_file, "r")
		filt_text_pat = file.read()
		file.close()
		file = None
	except:
		print("Error!!! File " + source_file + " could not be readed!")
		if (file != None):
			file.close()
		return 1
	
	
	# prepare the input for solution
	proj_list=""
	proj_list_pat='''Project("{##SOL_GUID##}") = "##PRJNAME##", "##PRJPATH##","{##PRJGUID##}"
EndProject
'''
	proj_configs=""
	proj_configs_pat='''    {##PRJGUID##}.Debug|Win32.ActiveCfg = Debug|Win32
    {##PRJGUID##}.Debug|Win32.Build.0 = Debug|Win32
    {##PRJGUID##}.Release|Win32.ActiveCfg = Release|Win32
    {##PRJGUID##}.Release|Win32.Build.0 = Release|Win32
'''
	sol_guid = str( uuid.uuid1() )
	
	# prepare input for projects
	incl_list_pat= '''    <ClInclude Include="##FILE_PATH##" />\n'''
	comp_list_pat= '''    <ClCompile Include="##FILE_PATH##" />\n'''
	other_list_pat= '''    <None Include="##FILE_PATH##" />\n'''

	
	for s_prj_file in prj_files:
		# clear reused vars
		incl_list = ""
		comp_list = ""
		other_list = ""
		
		# configuration type; may be changed
		config_type = ["Application"]
		
		# preparations
		prj_guid = str( uuid.uuid1() )
		s_prj_file = os.path.abspath( "../Qt/" + s_prj_file )
		s_prj_name = os.path.splitext( os.path.basename( s_prj_file ) )[0]
		prj_path = out_path + os.sep + s_prj_name
		print( "\tproject " + s_prj_name )
		
		# the first entry in solution file
		prj_cand = proj_list_pat.replace( "##PRJNAME##", s_prj_name )
		prj_cand = prj_cand.replace( "##PRJPATH##", s_prj_name + os.sep + s_prj_name + ".vcxproj" )
		prj_cand = prj_cand.replace( "##PRJGUID##", prj_guid )
		proj_list += prj_cand
		
		# the second entry in solution file
		prj_cand = proj_configs_pat.replace( "##PRJGUID##", prj_guid )
		proj_configs += prj_cand
		
		# read qt project
		file = None
		try:
			file = open(s_prj_file, "r")
			prj_qt = file.read()
			file.close()
			file = None
		except:
			print("Error!!! File " + s_prj_file + " could not be readed!")
			if (file != None):
				file.close()
			return 1
		
		# get the lists of headers and source files
		hd_list=[]
		src_list=[]
		res_lst = []
		form_lst = []
		other_lst = []
		incp_lst = []
	
		getQtPrjInfo( \
			prj_qt, hd_list, src_list, \
			res_lst, form_lst, other_lst, \
			incp_lst, config_type )

		adjustFilePaths( \
			s_prj_file,  \
			hd_list, src_list, \
			res_lst, form_lst, other_lst, \
			incp_lst )
		
		# content of filter file
		filt_text = ""


		filt_text = filt_text_pat.replace( "##ITEM_GROUPS##", \
			filtFile( prj_path, hd_list, src_list, res_lst, form_lst, other_lst ) \
		)
		
		# generate project lists
		for s_iter_f in hd_list:
			incl_list += incl_list_pat.replace( "##FILE_PATH##", \
				os.path.relpath( s_iter_f, prj_path ) )
		
		for s_iter_f in src_list:
			comp_list += comp_list_pat.replace( "##FILE_PATH##", \
				os.path.relpath( s_iter_f, prj_path ) )
		
		for s_iter_f in res_lst:
			other_list += other_list_pat.replace( "##FILE_PATH##", \
				os.path.relpath( s_iter_f, prj_path ) )
		for s_iter_f in form_lst:
			other_list += other_list_pat.replace( "##FILE_PATH##", \
				os.path.relpath( s_iter_f, prj_path ) )
		for s_iter_f in other_lst:
			other_list += other_list_pat.replace( "##FILE_PATH##", \
				os.path.relpath( s_iter_f, prj_path ) )
		
		
		inclp_text = ""
		for s_iter_f in incp_lst:
			inclp_text += s_iter_f + ";"
		print(config_type[0])

		prj_text = prj_text_pat
		prj_text = prj_text.replace( "##INC_DEBUG##", inclp_text)
		prj_text = prj_text.replace( "##INC_RELEASE##", inclp_text )
		prj_text = prj_text.replace( "##PRJGUID##", prj_guid )
		prj_text = prj_text.replace( "##PRJNAME##", s_prj_name )
		prj_text = prj_text.replace( "##CONFIG_TYPE##", config_type[0] )
		prj_text = prj_text.replace( "##INCL_LIST##", incl_list )
		prj_text = prj_text.replace( "##COMP_LIST##", comp_list )
		prj_text = prj_text.replace( "##OTHER_LIST##", other_list )
		
		
		
		# output the project
		if ( os.path.exists( prj_path ) == False ):
			os.mkdir( prj_path )
		#shutil.rmtree( prj_path )
		
		destination_file = os.path.abspath( prj_path + os.sep + s_prj_name + ".vcxproj")
		print ("\t\tWriting project file: " + destination_file)
		file = None
		try:
			file = open( destination_file, "w" )
			file.write( prj_text )
			file.close()
			file = None
		except:
			print("Error!!! File " + destination_file + " could not be writed!")
			if ( file != None ):
				file.close()
			return 1
			
		# output the filter
		destination_file=os.path.abspath( prj_path + "/" + s_prj_name + ".vcxproj.filters")
		print ("\t\tWriting filter file: " + destination_file)
		file = None
		try:
			file = open( destination_file, "w" )
			file.write( filt_text )
			file.close()
			file = None
		except:
			print("Error!!! File " + destination_file + " could not be writed!")
			if ( file != None ):
				file.close()
			return 1
		
		
	
	# finalise the solution text
	sol_text = sol_text.replace("##PROJECTS##",proj_list)
	sol_text = sol_text.replace("##PROJECT_CONFIGS##",proj_configs)
	sol_text = sol_text.replace("##SOL_GUID##",sol_guid)
	
	# output the solution
	destination_file=os.path.abspath( out_path + "/" + sol_name + ".sln")
	print ("Writing solution file: " + destination_file)
	file = None
	try:
		file = open(destination_file, "w")
		file.write(sol_text)
		file.close()
		file = None
	except:
		print("Error!!! File " + destination_file + " could not be writed!")
		if (file != None):
			file.close()
		return 1
		
	# exit happily
	return 0
	
#* ========================================================================= *#

	
#* ------------------------------------------------------------------------- *#
def	main():
	""" Main function that is called when the module is loaded by itself

	The function shall ask the user for input, then will generate the file(s)
	"""
	
	# present a simple logo
	showLogo()
	
	# this may be replaced by user input
	prj_lst = [
	"main_lib/main_lib.pro", 
	"starter/starter.pro"
	]
	
	# generate the solution
	return generateSol( "PlayGround", "../vs2010", prj_lst )

#* ========================================================================= *#

if __name__ == "__main__":
    sys.exit( main() )

#*  FUNCTIONS    =========================================================== *#
##
##
##
##
#* ------------------------------------------------------------------------- *#
#* ========================================================================= *#
